
#include "socket.h"

namespace tinkle {

Socket::Socket()
    : _sock(SOCKET_ERROR),
    _isTLSEnabled(false) {
    SSL_library_init();
    // create a new SSL_CTX object using TLS
    _sslCtx = SSL_CTX_new(TLSv1_method());
    // create a new SSL structure for connection
    _sslFd = SSL_new(_sslCtx);
}

Socket::~Socket() {
    if (!_sslFd) {
        SSL_free(_sslFd);
    }
    if (!_sslCtx) {
        SSL_CTX_free(_sslCtx);
    }
}

int Socket::Connect(const std::string &host, const std::string &port) {
    return -1;
}

int Socket::Read(char *buf, int len) {
    // use TLS stream
    if (_isTLSEnabled) {
        return SSL_read(_sslFd, buf, len);
    } else {    // do not use TLS stream
        return _NonTLSRead(buf, len);
    }
}

int Socket::Write(const char *buf, int len) {
    if (_isTLSEnabled) {
        return SSL_write(_sslFd, buf, len);
    } else {
        return _NonTLSWrite(buf, len);
    }
}

int Socket::EnableTLS() {
    if (SSL_get_fd(_sslFd) == -1) {
        // no file descriptor is linked to the ssl, try setting it
        if ((_sock != SOCKET_ERROR) && (SSL_set_fd(_sslFd, _sock) == 1)) {
            // linking succeed
            int ret = -1;
            // SSL_connect would fail while using non-blocking socket, -1
            // may be returned. Then what you can do is to wait and call
            // SSL_connect again until no error occurs. See openssl's documents
            // for more details.
            while ((ret = SSL_connect(_sslFd)) == -1) {
                int errCode = SSL_get_error(_sslFd, ret);
                switch (errCode) {
                    case SSL_ERROR_WANT_READ:
                    case SSL_ERROR_WANT_WRITE:
                        break;
                    default:
                        printf ("SSL_connect error\n");
                }
            }
            _isTLSEnabled = true;
            return true;
        }
        return false;
    }
    _isTLSEnabled = true;
    return true;
}

int Socket::DisableTLS() {
    _isTLSEnabled = false;
    return 1;
}

bool Socket::IsTLSEnabled() {
    return _isTLSEnabled;
}

}